package mapspinner.util;

import java.nio.IntBuffer;

import com.jme.util.geom.BufferUtils;

/**
 * LODUtil generates index buffers for various detail levels of map blocks it
 * also builds index buffers that can connect a given map block with a higher or
 * lower detail one.
 * 
 * heavily based on jmeterra's LODUtil code
 * 
 * @author James Vos
 */
public class LODUtil {
	public static final byte TOP = 8, BOT = 4, LEFT = 2, RIGHT = 1;
	public static final byte BUF_PER_LEVEL = 1 + LODUtil.TOP + LODUtil.BOT
			+ LODUtil.LEFT + LODUtil.RIGHT;

	/**
	 * Generates an index array at a certain detail level
	 * 
	 * @param verts
	 *            the vertex count
	 * @param level
	 *            the detail level of the indices to return
	 * @param sides
	 *            the sides that should run to a different detail level
	 * @return the indices that got generated.
	 */
	public static IntBuffer generateBuffer(final int verts, final int level,
			final byte sides) {
		final int tris = verts - 1;

		if (tris % level != 0) {
			System.out
					.println("Max detail squared must be at most half a blocks in tris.");
			System.exit(0);
		}

		int total = tris / level * (tris / level) * 6;
		final int reduction = tris / level / 2 * 3;

		if ((sides & LODUtil.TOP) != 0)
			total -= reduction;
		if ((sides & LODUtil.BOT) != 0)
			total -= reduction;
		if ((sides & LODUtil.LEFT) != 0)
			total -= reduction;
		if ((sides & LODUtil.RIGHT) != 0)
			total -= reduction;

		final IntBuffer indices = BufferUtils.createIntBuffer(total);

		final int one[] = new int[3];
		final int two[] = new int[3];
		boolean oneOn, twoOn;
		// go through entire array up to the second to last column.
		for (int x = 0; x < tris; x += level)
			for (int y = 0; y < tris; y += level) {
				oneOn = true;
				twoOn = true;

				one[0] = x + y * verts; // top left
				one[1] = x + level + verts * (y + level); // bottom right
				one[2] = x + level + y * verts; // top right

				two[0] = x + y * verts; // top left
				two[1] = x + verts * (y + level);// bottom left
				two[2] = x + level + verts * (y + level); // bottom right

				if ((sides & LODUtil.TOP) != 0 && y == 0)
					if (x / level % 2 == 0)
						// top right +1x
						one[2] += level;
					else {
						oneOn = false;
						two[2] = two[1];
						two[0] = one[1];
						two[1] = one[2];
					}

				if ((sides & LODUtil.BOT) != 0 && y == tris - level)
					if (x / level % 2 == 0) {
						twoOn = false;
						one[1] = two[1];
					} else
						// bottom left - 1x;
						two[1] -= level;

				if ((sides & LODUtil.LEFT) != 0 && x == 0)
					if (y / level % 2 == 0)
						// bottom left + 1y
						two[1] += verts * level;
					else {
						oneOn = false;
						two[0] = one[2];
					}

				if ((sides & LODUtil.RIGHT) != 0 && x == tris - level)
					if (y / level % 2 == 0) {
						twoOn = false;
						one[1] = two[1];
					} else
						one[2] -= verts * level;

				if (oneOn) {
					indices.put(one[0]);
					indices.put(one[1]);
					indices.put(one[2]);
				}

				if (twoOn) {
					indices.put(two[0]);
					indices.put(two[1]);
					indices.put(two[2]);
				}
			}
		return indices;
	}

	/**
	 * Generates a set of index arrays for use in LOD mipmapping of terrain
	 * blocks
	 * 
	 * @param blocksize
	 *            is the size of a terrain heightmap in width or breadth
	 * @num is the number of detail levels to generate
	 * @return an array of inbuffers that are to be used for index buffers.
	 */
	public static IntBuffer[] generateIndicis(final int blocksize, final int num) {
		int detail = 1;

		final IntBuffer[] retVal = new IntBuffer[num * LODUtil.BUF_PER_LEVEL
				+ 1];
		int count = 0;

		for (int k = 0; k <= num; k++) {
			retVal[count++] = LODUtil.generateBuffer(blocksize, detail,
					(byte) 0);
			if (k != num)
				for (byte x = 1; x < 16; x++)
					retVal[count++] = LODUtil.generateBuffer(blocksize, detail,
							x);
			detail += detail;
		}
		return retVal;
	}
}
